/*
 * Special KMS API Implement
 *
 * Note: the response data of the KMS request is CMS PKCS#7 Enveloped Data.
 * Specific format ref: https://tools.ietf.org/html/rfc5652#section-6
 *
 * Copyright (c) Huawei Technologies Co., Ltd. 2022. All Rights Reserved.
 * SPDX-License-Identifier: Apache-2.0.
 */
#include <string.h>
#include <stdlib.h>
#include <curl/curl.h>
#include <json-c/json.h>
#include <openssl/bio.h>
#include <openssl/evp.h>
#include <openssl/pem.h>
#include <glib.h>
#include "kms.h"
#include "cms.h"
#include "enclave_proxy.h"
#include "qtsm_lib.h"

#define RESERVED_SIZE                   2

static const func_API_name_t func_API_info = {
    .generate_data_key = {CREATE_DATAKEY, POST},
    .generate_random = {GEN_RANDOM, POST},
    .decrypt_data = {DECRYPT_DATA, POST}
};

static void print_curl_request(const char* url, const sig_params_t *req_params)
{
    unsigned int i;

    if (!req_params || !url) {
        fprintf(stderr, "the url or req_params is NULL\n");
        return;
    }

    fprintf(stdout, "\n****The main parameters of the curl request****\n");

    if ((req_params->method.data != NULL) && req_params->method.len > 0) {
        fprintf(stdout, "method :\t");
        for (i = 0; i < req_params->method.len; i++)
            fprintf(stdout, "%c", req_params->method.data[i]);
        fprintf(stdout, "\n");
    } else {
        fprintf(stderr, "the method of req_params is error\n");
    }

    fprintf(stdout, "url : %s\n", url);

    fprintf(stdout, "Headers :\n");
    for (i = 0; i < req_params->headers.len; i++) {
        char header[URL_MAX_LEN] = {0};
        sig_snprintf(header, URL_MAX_LEN, "%V: %V\t",
            &(req_params->headers.data[i].name),
            &(req_params->headers.data[i].value));
        fprintf(stdout, "%s", header);
    }
    fprintf(stdout, "\n");

    fprintf(stdout, "Payload :\n");
    if (req_params->payload.data && req_params->payload.len > 0) {
        for (i = 0; i < req_params->payload.len; i++)
            fprintf(stdout, "%c", req_params->payload.data[i]);
        fprintf(stdout, "\n");
    }

    fprintf(stdout, "*******End of the curl request******\n");
}

/*
 * write_memory_callback() - Callback function for curl request.
 * @contents (input): The delivered data.
 * @size (input): Always is 1.
 * @nmemb (input): The size of content data.
 * @userp (output): The response to KMS request.
 *
 * Context: process context
 *
 * Returns: the size of content data.
 */
static size_t write_memory_callback(void *contents,
    size_t size, size_t nmemb, void *userp)
{
    size_t real_size = size * nmemb;
    sig_str_t *resp_data = (sig_str_t *)userp;
    resp_data->data = malloc(real_size + 1);
    if (resp_data->data == NULL)
        return -1;

    resp_data->len = real_size + 1;

    memset(resp_data->data, 0, real_size + 1);

    memcpy(resp_data->data, (char*)contents, real_size);
    resp_data->data[real_size] = '\0';
    return real_size;
}

static CURLcode set_curl_common_opt(CURL *curl)
{
    CURLcode rc;

    /* debug mode */
    rc = curl_easy_setopt(curl, CURLOPT_VERBOSE, 1);
    if (rc != CURLE_OK)
        return rc;

    /* Use unix socket proxy */
    rc = curl_easy_setopt(curl, CURLOPT_UNIX_SOCKET_PATH, CURL_SOCKET_PATH);
    if (rc != CURLE_OK)
        return rc;

    /* time out */
    rc = curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
    if (rc != CURLE_OK)
        return rc;

    rc = curl_easy_setopt(curl, CURLOPT_TIMEOUT_MS, CURL_TIME_OUT_MS);
    if (rc != CURLE_OK)
        return rc;

    rc = curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0);
    if (rc != CURLE_OK)
        return rc;

    rc = curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0);
    if (rc != CURLE_OK)
        return rc;

    rc = curl_easy_setopt(curl, CURLOPT_NOBODY, 0L);
    if (rc != CURLE_OK)
        return rc;

    return curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION,
        write_memory_callback);
}

static unsigned long set_curl_spec_opt(CURL *curl, sig_params_t *req_params,
    const sig_str_t* fun_name, sig_str_t* respon_data, CURLcode *curl_code,
    struct curl_slist **chunk)
{
    unsigned int new_uri_len;
    unsigned int i;
    char *url;
    int rc;

    /* Get request uri */
    new_uri_len = req_params->uri_prefix.len + fun_name->len + RESERVED_SIZE;
    char uri[new_uri_len];
    memset(uri, 0, sizeof(uri));

    if (snprintf(uri, new_uri_len - 1, "%s%s",
                 req_params->uri_prefix.data, fun_name->data) < 0)
        return KMS_COMMON_ERROR;
    uri[new_uri_len - 1] = '\0';
    req_params->uri = (sig_str_t)sig_str(uri);

    /*
     * Generate signature. generated authorization header is
     * inserted into params.headers
     */
    if (sig_sign(req_params) != 0)
        return KMS_REQ_PARAMS_ERROR;

    *curl_code = curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, req_params->method.data);
    if (*curl_code != CURLE_OK)
        return KMS_CURL_ERROR;

    if ((url = malloc(URL_MAX_LEN)) == NULL)
        return KMS_BUFFS_ERROR;
    memset(url, 0, URL_MAX_LEN);
    sig_snprintf(url, URL_MAX_LEN, "https://%V%V?%V", &(req_params->host),
        &(req_params->uri), &(req_params->query_str));

    *curl_code = curl_easy_setopt(curl, CURLOPT_URL, url);
    if (*curl_code != CURLE_OK) {
        rc = KMS_CURL_ERROR;
        goto error;
    }

    for (i = 0; i < req_params->headers.len; i++) {
        char *header = malloc(URL_MAX_LEN);
        if (header == NULL) {
            rc = KMS_BUFFS_ERROR;
            goto error;
        }
        memset(header, 0, URL_MAX_LEN);
        sig_snprintf(header, URL_MAX_LEN, "%V: %V",
            &(req_params->headers.data[i].name),
            &(req_params->headers.data[i].value));
        *chunk = curl_slist_append(*chunk, header);
        g_free(header);
    }

    *curl_code = curl_easy_setopt(curl, CURLOPT_HTTPHEADER, *chunk);
    if (*curl_code != CURLE_OK) {
        rc = KMS_CURL_ERROR;
        goto error;
    }

    *curl_code = curl_easy_setopt(curl, CURLOPT_POSTFIELDS, req_params->payload.data);
    if (*curl_code != CURLE_OK) {
        rc = KMS_CURL_ERROR;
        goto error;
    }

#ifdef SDK_DEBUG
    print_curl_request(url, req_params);
#endif

    rc = KMS_SUCCESS;
error:
    g_free(url);
    return rc;
}

static unsigned long resp_json(unsigned long status, const char* data)
{
    struct json_object *resp_json = NULL;

#ifdef SDK_DEBUG
    qtsm_sdk_debug("status is %lu\n", status);
#endif

    if (status == HTTPS_OK)
        return KMS_SUCCESS;

    resp_json = json_tokener_parse(data);
    if (resp_json != NULL) {
        fprintf(stderr, "HTTPS status: %lu\nKMS response content:\n%s\n",
                status, data);
        json_object_put(resp_json);
    } else {
        fprintf(stderr, "HTTPS status: %lu\n", status);
    }

    return KMS_REQ_FAILED;
}

/*
 * perform_request_AKSK() - The common interface to send AKSK request.
 * @req_params (input): Request parameters.
 * @fun_name (input): Specific KMS request in @func_API_info.
 * @respon_data (output): The response to KMS request.
 *
 * Context: process context
 *
 * Returns: Status codes for HTTPs requests.
 */
static unsigned long perform_request_AKSK(sig_params_t *req_params,
                                          const sig_str_t* fun_name,
                                          sig_str_t* respon_data)
{
    CURL *curl = NULL;
    CURLcode rc;
    struct curl_slist *chunk = NULL;
    unsigned long status;

    /* Send https request using curl library */
    rc = curl_global_init(CURL_GLOBAL_ALL);
    if (rc != CURLE_OK)
        return KMS_INTERNAL_ERROR;

    curl = curl_easy_init();
    if (!curl) {
        curl_global_cleanup();
        return KMS_INTERNAL_ERROR;
    }

    rc = set_curl_common_opt(curl);
    if (rc != CURLE_OK)
        goto error;

    status = set_curl_spec_opt(curl, req_params,
        fun_name, respon_data, &rc, &chunk);
    if (status != KMS_SUCCESS || rc != CURLE_OK)
        goto error;

    rc = curl_easy_setopt(curl, CURLOPT_WRITEDATA,
        (void *)&(respon_data->data));
    if (rc != CURLE_OK)
        goto error;

    rc = curl_easy_perform(curl);

error:
    if (chunk)
        curl_slist_free_all(chunk);

    if (rc != CURLE_OK) {
        fprintf(stderr, "perform_request_AKSK() failed: %s\n",
            curl_easy_strerror(rc));
        curl_easy_cleanup(curl);
        curl_global_cleanup();
        return KMS_CURL_ERROR;
    }

    curl_easy_getinfo(curl, CURLINFO_HTTP_CODE, &status);
    curl_easy_cleanup(curl);
    curl_global_cleanup();

    return resp_json(status, respon_data->data);
}

/*
 * perfom_func_APIs() - Used to handle specific KMS requests.
 * @params_global (input): Request parameters.
 * @func_params (input): Parameters for specific KMS requests.
 * @payload (input): The payload field of the HTTPs requests.
 * @respon_data (output): The response to KMS request.
 *
 * Context: process context
 *
 * Returns: Status codes for HTTPs requests.
 */
static unsigned long perfom_func_APIs(const sig_params_t *params_global,
    const func_params_t *func_params, char *payload,
    sig_str_t *respon_data)
{
    sig_params_t cur_req;
    sig_str_t name;
    unsigned long status;

    cur_req = *params_global;
    cur_req.method = (sig_str_t)sig_str(func_params->method);
    cur_req.payload = (sig_str_t)sig_str(payload);
    cur_req.query_str = (sig_str_t)sig_str("");
    name = (sig_str_t)sig_str(func_params->name);

    sig_headers_add(&cur_req.headers, "Content-Type", "application/json");
    status = perform_request_AKSK(&cur_req, &name, respon_data);
    sig_headers_free(&cur_req.headers);

#ifdef SDK_DEBUG
    qtsm_sdk_debug("status is %lu\n", status);
#endif

    return status;
}

static unsigned long handle_check(const keyid_handle_t *handle)
{
    if (handle == NULL || handle->key_id == NULL || handle->len == 0)
        return KMS_COMMON_ERROR;

    return KMS_SUCCESS;
}

static void free_heap_sig_str(sig_str_t *str)
{
    if (!str)
        return;

    if (str->data != NULL) {
        free(str->data);
        str->data = NULL;
    }
    str->len = 0;
}

static void stream_buf_cleanup(struct stream_buf *buf)
{
    if (buf->buffer)
        free(buf->buffer);

    buf->buffer = NULL;
    buf->capacity = 0;
    buf->len = 0;
}

unsigned int extract_data_from_envelop(unsigned char *ciphertext,
    unsigned int ciphertext_len, struct rsa_keypair* keypair,
    struct stream_buf *plain_text)
{
    struct stream_buf respon_data;
    struct stream_buf cipher_symm_key = { 0 };
    struct stream_buf plain_symm_key = { 0 };
    struct stream_buf iv = { 0 };
    struct stream_buf cipher_text = { 0 };
    int rc;
    if (ciphertext == NULL || ciphertext_len == 0 || keypair == NULL)
        return KMS_RESP_ERROR;

    respon_data.len = ciphertext_len;
    respon_data.capacity = respon_data.len;
    respon_data.buffer = ciphertext;
    /* Parse the Enveloped data */
    rc = parse_response_enveloped_data(&respon_data, &cipher_symm_key,
        &iv, &cipher_text);
    if (rc != CMS_SUCCESS) {
        fprintf(stderr, "Cannot parse CMS enveloped data!\n");
        return rc;
    }

    /* Decrypt symm_key using rsa key */
    rc = attestation_rsa_decrypt(keypair, &cipher_symm_key, &plain_symm_key);
    if (rc != CMS_SUCCESS)
        goto clean;

    /* Decrypt cipher random data using symm key */
    rc = response_cipher_decrypt(&cipher_text, &plain_symm_key, &iv, plain_text);
    if (rc != CMS_SUCCESS) {
        fprintf(stderr, "Cannot decrypt CMS encrypted content!\n");
        goto clean;
    }

    stream_buf_cleanup(&plain_symm_key);

clean:
    stream_buf_cleanup(&cipher_symm_key);
    stream_buf_cleanup(&iv);
    stream_buf_cleanup(&cipher_text);
    return rc;
}

static int kms_attestation_doc_to_json(struct stream_buf *att_doc,
    struct rsa_keypair **user_pubkey, char **att_enbase64,
    unsigned int *att_enbase64_len)
{
    int rc;

    /* Allocate memory for storing attestation doc */
    att_doc->capacity = att_doc->len = sizeof(struct attestation_document);
    att_doc->buffer = malloc(att_doc->capacity);
    if (!(att_doc->buffer))
        return KMS_BUFFS_ERROR;

    memset(att_doc->buffer, 0, att_doc->capacity);

    /* Get RSA keypair for attestation doc */
    /* Todo : rsa size as input param */
    *user_pubkey = attestation_rsa_keypair_new(RSA_2048);
    if (!(*user_pubkey)) {
        stream_buf_cleanup(att_doc);
        return KMS_RSA_ERROR;
    }

    /* Get attestation doc */
    rc = get_attestation_doc(*user_pubkey, att_doc);
    if (rc != NO_ERROR) {
        stream_buf_cleanup(att_doc);
        attestation_rsa_keypair_destroy(*user_pubkey);
        return rc;
    }

    /* Add attestation doc to base64 */
    *att_enbase64 = g_base64_encode(att_doc->buffer, att_doc->len);
    if (!(*att_enbase64)) {
        stream_buf_cleanup(att_doc);
        attestation_rsa_keypair_destroy(*user_pubkey);
        return KMS_INTERNAL_ERROR;
    }
    *att_enbase64_len = strlen(*att_enbase64);

    return KMS_SUCCESS;
}

static unsigned long parse_kms_gen_datakey_resp(const sig_str_t *respon_data,
    struct rsa_keypair* keypair, datakey_t *datakey)
{
    struct json_object *resp_json = NULL, *res_data = NULL;
    unsigned int res_data_len, status;
    gsize datakey_base64_len;
    guchar *datakey_base64 = NULL;
    struct stream_buf plain_text;

    /* Decode cjson */
    resp_json = json_tokener_parse(respon_data->data);
    if (!resp_json) {
        fprintf(stderr, "cjson_parse response data failed\n");
        return KMS_CJSON_ERROR;
    }

    /* Get key_id from respon_data */
    if (!json_object_object_get_ex(resp_json, "key_id", &res_data)) {
        fprintf(stderr, "failed to get key_id object from response data\n");
        status = KMS_CJSON_ERROR;
        goto err;
    }
    res_data_len = strlen(json_object_get_string(res_data));
    if (*(datakey->key_id_len) < res_data_len)
        return KMS_BUFFS_SMALL;

    *(datakey->key_id_len) = res_data_len;
    memcpy(datakey->key_id, json_object_get_string(res_data), res_data_len);

    /* Get cipher_text from respon_data */
    if (!json_object_object_get_ex(resp_json, "cipher_text", &res_data)) {
        fprintf(stderr, "failed to get cipher_text object from response data\n");
        status = KMS_CJSON_ERROR;
        goto err;
    }
    res_data_len = strlen(json_object_get_string(res_data));
    if (*(datakey->cipher_key_len) < res_data_len)
        return KMS_BUFFS_SMALL;

    *(datakey->cipher_key_len) = res_data_len;
    memcpy(datakey->cipher_key, json_object_get_string(res_data), res_data_len);

    /* Get ciphertext_recipient from respon_data */
    if (!json_object_object_get_ex(resp_json, "ciphertext_recipient", &res_data)) {
        fprintf(stderr, "failed to get ciphertext_recipient item from response data\n");
        status = KMS_CJSON_ERROR;
        goto err;
    }

    datakey_base64 = g_base64_decode(json_object_get_string(res_data), &datakey_base64_len);
    if (!datakey_base64) {
        fprintf(stderr, "failed to base64_decode from response data\n");
        status = KMS_CJSON_ERROR;
        goto err;
    }

    plain_text.buffer = datakey->plain_key;
    plain_text.capacity = plain_text.len = *(datakey->plain_key_len);
    /* Extract true random number from enveloped data. */
    status = extract_data_from_envelop(datakey_base64, datakey_base64_len, keypair, &plain_text);

    g_free(datakey_base64);
err:
    if (resp_json)
        json_object_put(resp_json);
    return status;
}

unsigned long kms_generate_datakey_blocking(const sig_params_t *sig_params,
                                            const keyid_handle_t *handle,
                                            unsigned int datakey_len,
                                            datakey_t *datakey)
{
    sig_str_t respon_data = {NULL, 0};
    unsigned int payload_len;
    struct rsa_keypair *user_pubkey = NULL;
    struct stream_buf att_doc = {0};
    char handle_keyid[handle->len + 1];
    gchar *att_enbase64 = NULL;
    unsigned int att_enbase64_len = 0;
    unsigned long status;
    int rc;

    if (handle_check(handle) != KMS_SUCCESS)
        return KMS_HANDLE_EMPTY;

    if (datakey == NULL ||
        datakey->plain_key == NULL ||
        *(datakey->plain_key_len) == 0 ||
        datakey->cipher_key == NULL ||
        *(datakey->cipher_key_len) == 0)
        return KMS_BUFFS_EMPTY;

    rc = kms_attestation_doc_to_json(&att_doc, &user_pubkey, &att_enbase64,
        &att_enbase64_len);
    if (rc != KMS_SUCCESS)
        return KMS_GET_ATTESTATION_ERROR;

    /* Add key_id, datakey_length, att doc */
    payload_len = att_enbase64_len + handle->len + PAYLOAD_EXPAND_SIZE;
    char payload[payload_len];
    memset(handle_keyid, 0, sizeof(handle_keyid));
    memset(payload, 0, sizeof(payload));
    memcpy(handle_keyid, handle->key_id, handle->len);
    handle_keyid[handle->len] = '\0';

    if (snprintf(payload, payload_len - 1,
                 "{\"key_id\": \"%s\", \"datakey_length\": \"%u\", "
                 "\"recipient\": {\"attestation_document\": \"%s\", "
                 "\"key_encryption_algorithm\": \"RSAES_OAEP_SHA_256\"}}",
                 handle_keyid, datakey_len, att_enbase64) < 0)
        return KMS_COMMON_ERROR;
    payload[payload_len - 1] = '\0';

    status = perfom_func_APIs(sig_params, &(func_API_info.generate_data_key),
        payload, &respon_data);
#ifdef SDK_DEBUG
    qtsm_sdk_debug("status of perfom_func_APIs is %lu\n", status);
#endif

    /* Parse response data */
    if (status == KMS_SUCCESS)
        status = parse_kms_gen_datakey_resp(&respon_data, user_pubkey, datakey);

    free_heap_sig_str(&respon_data);
    stream_buf_cleanup(&att_doc);
    g_free(att_enbase64);
    attestation_rsa_keypair_destroy(user_pubkey);
    return status;
}

static unsigned long parse_kms_gen_random_resp(const sig_str_t *respon_data,
    struct rsa_keypair* keypair, random_data_t *random)
{
    struct json_object *resp_json = NULL;
    struct json_object *res_data = NULL;
    unsigned int status;
    gsize random_base64_len;
    guchar *random_base64 = NULL;
    struct stream_buf plain_text;

    /* Decode cjson */
    resp_json = json_tokener_parse(respon_data->data);
    if (!resp_json) {
        fprintf(stderr, "cjson_parse response data failed\n");
        return KMS_CJSON_ERROR;
    }

    /* Get random_data from respon_data */
    if (!json_object_object_get_ex(resp_json, "ciphertext_recipient", &res_data)) {
        fprintf(stderr, "failed to get random_data object from response data\n");
        status = KMS_CJSON_ERROR;
        goto err;
    }

    random_base64 = g_base64_decode(json_object_get_string(res_data),
        &random_base64_len);
    if (!random_base64) {
        fprintf(stderr, "failed to base64_decode from response data\n");
        status = KMS_CJSON_ERROR;
        goto err;
    }

    plain_text.buffer = random->random;
    plain_text.capacity = plain_text.len = *(random->random_len);
    /* Extract true random number from enveloped data. */
    status = extract_data_from_envelop(random_base64, random_base64_len, keypair,
        &plain_text);

    g_free(random_base64);
err:
    if (resp_json)
        json_object_put(resp_json);
    return status;
}

unsigned long kms_gen_random_blocking(const sig_params_t *sig_params,
    unsigned int random_len, random_data_t *random)
{
    sig_str_t respon_data = {NULL, 0};
    unsigned int payload_len;
    struct rsa_keypair *user_pubkey = NULL;
    struct stream_buf att_doc = {0};
    char *att_enbase64 = NULL;
    unsigned int att_enbase64_len = 0;
    unsigned long status;
    int rc;

    if (random == NULL || random->random == NULL || *(random->random_len) == 0)
        return KMS_BUFFS_EMPTY;

    rc = kms_attestation_doc_to_json(&att_doc, &user_pubkey,
        &att_enbase64, &att_enbase64_len);
    if (rc != KMS_SUCCESS)
        return KMS_GET_ATTESTATION_ERROR;

    /* Add key_id, datakey_length, att doc */
    payload_len = att_enbase64_len + PAYLOAD_EXPAND_SIZE;
    char payload[payload_len];
    memset(payload, 0, sizeof(payload));

    if (snprintf(payload, payload_len - 1, "{\"random_data_length\": \"%u\", "
                 "\"recipient\": {\"attestation_document\": \"%s\", "
                 "\"key_encryption_algorithm\": \"RSAES_OAEP_SHA_256\"}}",
                 random_len, att_enbase64) < 0)
        return KMS_COMMON_ERROR;
    payload[payload_len - 1] = '\0';

    status = perfom_func_APIs(sig_params, &(func_API_info.generate_random),
        payload, &respon_data);
#ifdef SDK_DEBUG
    qtsm_sdk_debug("status of perfom_func_APIs is %lu\n", status);
#endif

    /* Parse response data */
    if (status == KMS_SUCCESS)
        status = parse_kms_gen_random_resp(&respon_data, user_pubkey, random);

    free_heap_sig_str(&respon_data);
    stream_buf_cleanup(&att_doc);
    free(att_enbase64);
    attestation_rsa_keypair_destroy(user_pubkey);
    return status;
}

static unsigned long parse_kms_decrypt_data_resp(const sig_str_t *respon_data,
    struct rsa_keypair* keypair, plain_cipher_buff_t *data)
{
    struct json_object *resp_json = NULL;
    struct json_object *res_data = NULL;
    unsigned int status;
    gsize data_base64_len;
    guchar *data_base64 = NULL;
    struct stream_buf plain_text;

    /* Decode cjson */
    resp_json = json_tokener_parse(respon_data->data);
    if (!resp_json) {
        fprintf(stderr, "cjson_parse response data failed\n");
        return KMS_CJSON_ERROR;
    }

    /* Get cipher_text from respon_data */
    if (!json_object_object_get_ex(resp_json, "ciphertext_recipient", &res_data)) {
        fprintf(stderr, "failed to get cipher_text object from response data\n");
        status = KMS_CJSON_ERROR;
        goto err;
    }

    data_base64 = g_base64_decode(json_object_get_string(res_data),
        &data_base64_len);
    if (!data_base64) {
        fprintf(stderr, "failed to base64_decode from response data\n");
        status = KMS_CJSON_ERROR;
        goto err;
    }

    plain_text.buffer = data->data_out;
    plain_text.capacity = plain_text.len = *(data->data_out_len);
    memset(plain_text.buffer, 0, plain_text.capacity);
    /* Extract true random number from enveloped data. */
    status = extract_data_from_envelop(data_base64, data_base64_len, keypair,
        &plain_text);

    g_free(data_base64);
err:
    if (resp_json)
        json_object_put(resp_json);
    return status;
}

unsigned long kms_decrypt_data_blocking(const sig_params_t *sig_params,
                                        const keyid_handle_t *handle,
                                        plain_cipher_buff_t *data)
{
    sig_str_t respon_data = {NULL, 0};
    unsigned int payload_len;
    struct rsa_keypair *user_pubkey = NULL;
    struct stream_buf att_doc = {0};
    char handle_keyid[handle->len + 1];
    gchar *att_enbase64 = NULL;
    unsigned int att_enbase64_len = 0;
    unsigned long status;
    int rc;

    if (handle_check(handle) != KMS_SUCCESS)
        return KMS_HANDLE_EMPTY;

    if (data == NULL ||
        data->data_in == NULL ||
        data->data_in_len == 0 ||
        data->data_out == NULL ||
        *(data->data_out_len) == 0)
        return KMS_BUFFS_EMPTY;

    if (strlen(data->data_in) != data->data_in_len)
        return KMS_REQ_PARAMS_ERROR;

    rc = kms_attestation_doc_to_json(&att_doc, &user_pubkey,
        &att_enbase64, &att_enbase64_len);
    if (rc != KMS_SUCCESS)
        return KMS_GET_ATTESTATION_ERROR;

    /* Add key_id, datakey_length, att doc */
    payload_len = att_enbase64_len + handle->len +
        data->data_in_len + PAYLOAD_EXPAND_SIZE;

    char payload[payload_len];
    memset(handle_keyid, 0, sizeof(handle_keyid));
    memset(payload, 0, sizeof(payload));
    memcpy(handle_keyid, handle->key_id, handle->len);
    handle_keyid[handle->len] = '\0';

    if (snprintf(payload, payload_len - 1, "{\"key_id\": \"%s\", \"cipher_text\": \"%s\", "
                 "\"recipient\": {\"attestation_document\": \"%s\", "
                 "\"key_encryption_algorithm\": \"RSAES_OAEP_SHA_256\"}}",
                 handle_keyid, data->data_in, att_enbase64) < 0)
        return KMS_COMMON_ERROR;

    payload[payload_len - 1] = '\0';

    status = perfom_func_APIs(sig_params, &(func_API_info.decrypt_data),
        payload, &respon_data);
#ifdef SDK_DEBUG
    qtsm_sdk_debug("status of perfom_func_APIs is %lu\n", status);
#endif

    /* Parse response data */
    if (status == KMS_SUCCESS)
        status = parse_kms_decrypt_data_resp(&respon_data, user_pubkey, data);

    free_heap_sig_str(&respon_data);
    stream_buf_cleanup(&att_doc);
    free(att_enbase64);
    attestation_rsa_keypair_destroy(user_pubkey);
    return status;
}
